﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using IRemoting;
using System.Windows.Forms;

namespace QLNH_SERVER
{
    public class Manager
    {
        private MainForm mainForm;
        private TcpListener listener;
        private List<User> userList;

        public Manager(MainForm mainForm, TcpListener listener)
        {
            this.mainForm = mainForm;
            this.listener = listener;
            userList = new List<User>();
        }

        public void work()
        {
            Thread t = new Thread(new ThreadStart(doWork));
            t.SetApartmentState(ApartmentState.MTA);
            t.IsBackground = true;
            t.Start();
        }

        private void doWork()
        {
            while (true)
            {
                Socket socket = listener.AcceptSocket();
                Worker worker = new Worker(this, socket);
                worker.work();
            }
        }

        private bool isLoggedUser(string userName)
        {
            return userList.Find(e => e.getUserName().ToLower().Equals(userName.ToLower())) != null;
        }

        private void addUser(string userName, Socket socket)
        {
            userList.Add(new User(userName.ToLower(), socket));
            mainForm.showTotalOnline(userList.Count);
        }

        private void removeUser(string userName)
        {
            var user = userList.Find(e => e.getUserName().ToLower().Equals(userName.ToLower()));
            if (user != null)
            {
                userList.Remove(user);
                mainForm.showTotalOnline(userList.Count);
            }
        }

        public void clearUsers()
        {
            userList.Clear();
            mainForm.showTotalOnline(userList.Count);
        }

        public void sendBroadcastMassage(BasePackage package)
        {
            List<User> disconnectUsers = new List<User>();

            for (int i = 0; i < userList.Count; i++)
            {
                try
                {
                    NetworkStream stream = new NetworkStream(userList[i].getSocket());
                    StreamWriter writer = new StreamWriter(stream);
                    writer.AutoFlush = true;

                    writer.WriteLine(package.convertToString());
                }
                catch
                {
                    disconnectUsers.Add(userList[i]);
                    continue;
                }
            }

            if (disconnectUsers.Count > 0)
            {
                for (int i = 0; i < disconnectUsers.Count; i++)
                {
                    userList.Remove(disconnectUsers[i]);
                }

                mainForm.showTotalOnline(userList.Count);
            }
        }

        public class Worker
        {
            private Manager manager;
            private Socket socket;

            public Worker(Manager manager, Socket socket)
            {
                this.manager = manager;
                this.socket = socket;
            }

            public void work()
            {
                Thread t = new Thread(new ThreadStart(doWork));
                t.SetApartmentState(ApartmentState.MTA);
                t.IsBackground = true;
                t.Start();
            }

            private void doWork()
            {
                var stream = new NetworkStream(socket);

                var reader = new StreamReader(stream);
                var writer = new StreamWriter(stream);
                writer.AutoFlush = true;


                while (true)
                {
                    string message = null;
                    try
                    {
                        message = reader.ReadLine();
                    }
                    catch
                    {
                        break;
                    }

                    if (message == null)
                    {
                        break;
                    }

                    BasePackage package = new BasePackage();
                    package.convertFromString(message);
                    

                    string userName = "";

                    switch (package.getHeader())
                    {
                        case PackageHeader.LOGIN_REQ:
                            // Login request

                            userName = package.getUsername();

                            BasePackage reply = new BasePackage();
                            reply.setHeader(PackageHeader.LOGIN_REQ_REP);

                            if (manager.isLoggedUser(userName))
                            {
                                // If user is logged
                                reply.setLogginResult(false);
                                reply.setLogginReplyMessage("* Tài khoản đã đăng nhập");

                                try
                                {
                                    writer.WriteLine(reply.convertToString());
                                }
                                catch
                                {
                                    return;
                                }
                            }
                            else
                            {
                                reply.setLogginResult(true);

                                try
                                {
                                    writer.WriteLine(reply.convertToString());
                                }
                                catch
                                {
                                    return;
                                }

                                manager.addUser(package.getUsername(), socket);
                            }

                            break;

                        
                        case PackageHeader.LOGOUT_REQ:
                            // Log out request

                            userName = package.getUsername();

                            if (manager.isLoggedUser(userName))
                            {
                                // If user is logged
                                manager.removeUser(userName);
                            }

                            break;

                        case PackageHeader.SEND_BROADCAST_MESSAGE:
                            // Send broadcast message

                            manager.sendBroadcastMassage(package);
                            
                            break;
                        default:
                            break;
                    }
                }
            }
        }
    }
}
